//1⃣️1⃣️1⃣️1⃣️1⃣️1⃣️-2.1基础部分-1⃣️1⃣️1⃣️1⃣️1⃣️

/*
//声明一个类型为 String ，名字为 welcomeMessage 的变量
let welcomMessage:String
welcomMessage = "Hello"
//可以在一行中定义多个同样类型的变量，用逗号分割，并在最后一个变量名之后添加类型标注
var red,green,blue:Double

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹常量和变量的命名:你可以用任何你喜欢的字符作为常量和变量名，包括 Unicode 字符
//命名规则：常量与变量名不能包含数学符号，箭头，保留的（或者非法的）Unicode 码位，连线与制表符。也不能以数字开头，但是可以在常量与变量名的其他地方包含数字
let π = 3.14
let 您好 = "你好世界"
let 🐶🐶 = "dogcow"

//你可以更改现有的变量值为其他同类型的值
var friendlyWelcome = "Hello"
friendlyWelcome = "bonjuur"

//与变量不同，常量的值一旦被确定就不能更改了
let languageName = "swif"
//languageName = "swift++"//报错

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹输出常量和变量
print(friendlyWelcome)
print("The current value of friendlyWelcome is \(friendlyWelcome)")

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹整数范围
let minValue = UInt8.min//minValue为0，是UInt8类型
let maxValue = UInt8.max//maxValue为255，是UInt8类型

//浮点数,Double表示64位浮点数,Float
//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹类型安全和类型推断
let meaningOfLife = 42// meaningOfLife 会被推测为 Int 类型,因为你给它赋的初始值看起来像一个整数
let pi = 3.14159//pi 会被推测为 Double 类型
let anotherPi = 3 + 0.14159 //如果表达式中同时出现了整数和浮点数，会被推断为 Double 类型

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹数值型字面凉
//整数字面量可以被写作：一个十进制数，没有前缀;一个二进制数，前缀是0b;一个八进制数，前缀是0o;一个十六进制数，前缀是0x
//下面的所有整数字面量的十进制值都是17
let decimaInterger = 17
let binaryInter = 0b10001//二进制的17
let octalInterger = 0o21//八进制的17
let hexadecimalInterger = 0x11//十六进制的17

//数值类字面量可以包括额外的格式来增强可读性。整数和浮点数都可以添加额外的零并且包含下划线，并不会影响字面量
let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillon = 1_000_000.000_000_1

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹数值型类型转换
//Int8类型的常量或者变量可以存储的数字范围是-128~127，而UInt8类型的常量或者变量能存储的数字范围是0~255
//let cannotBeNegative:UInt8 = -1// UInt8 类型不能存储负数，所以会报错
//let tooBig:Int8 = Int8.max + 1//Int8 类型不能存储超过最大值的数，所以会报错
let twoThousand:UInt16 = 2_000
let one:UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)//现在两个数字的类型都是 UInt16，可以进行相加。目标常量 twoThousandAndOne 的类型被推断为 UInt16，因为它是两个 UInt16 值的和。


//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹整数和浮点数转换
let three = 3
let pointOneFourOneFiveNine = 0.14159
let pii = Double(three) + pointOneFourOneFiveNine//加号两边的数类型须相同;如果不进行转换，两者无法相加
let integerPi = Int(pii)// integerPi 等于 3，所以被推测为 Int 类型;当用这种方式来初始化一个新的整数值时，浮点值会被截断。也就是说 4.75 会变成 4，-3.9 会变成 -3;结合数字类常量和变量不同于结合数字类字面量。字面量3可以直接和字面量0.14159相加，因为数字字面量本身没有明确的类型。它们的类型只在编译器需要求值的时候被推测

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹布尔值
let orangesAreOrange = true
let turnipsAreDelicious = false
if turnipsAreDelicious {
    print("Mmm .tasy turnips!")
}else{
    print("Eww,turnips are horrible")
}
/*
let i = 1
if i {
    //不会通过编译
}
*/
/*
let i = 1
if i == 1 {
    print("Success")//便已成功
}
*/

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹元组:把多个值组合成一个复合值。元组内的值可以是任意类型，并不要求是相同类型
let http404Error = (404,"Not Found")// http404Error 的类型是 (Int, String)，值是 (404, "Not Found")
let (statusCode,statusMessage) = http404Error
print("The status code is \(statusCode)")
print("The status message is \(statusMessage)")

//如果你只需要一部分元组值，分解的时候可以把忽略的部分用下划线（_）标记：
let (justTheStatusCode,_) = http404Error
print("The status code is \(justTheStatusCode)")

//此外还可以通过下标来访问元组中的单个元素， 下标从零开始：
print("The status code is \(http404Error.0)")
print("The status message is \(http404Error.1)")

//可以在定义元组的时候给单个元素命名：
let http200Status = (statusCode:200,description:"ok")

//给元组中的元素命名后，你可以通过名字来获取这些元素的值：
print("The status code is \(http200Status.statusCode)")
print("The status message is \(http200Status.description)")

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹可选类型:可选类型表示：有值，等于x;没有值
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
//convertedNumber被推测为类型"Int"，或者类型"optional Int"

//----------nil
var serverResponseCode:Int? = 404//serverResponseCode包含一个可选的Int值404
serverResponseCode = nil //serverResponseCode现在不包含值
//nil不能用于非可选的常量和变量。如果代码中有常量或者变量需要处理值缺失的情况，请把它们声明称对应的可选类型

//如果声明一个可选常量或者变量但是没有赋值，它们会自动被设置为nil
var surveyAnswer:String?//surveyAnswer被自动设置为nil
/*
 注意：
 Swift 的 nil 和 Objective-C 中的 nil 并不一样。在 Objective-C 中，nil 是一个指向不存在对象的指针。在 Swift 中，nil 不是指针——它是一个确定的值，用来表示值缺失。任何类型的可选状态都可以被设置为 nil，不只是对象类型。
 */

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹if语句以及强制解析

if convertedNumber != nil {
    print("convertedNumber contains some integer value.")
}

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹可选绑定

if let actualNumber = Int(possibleNumber) {//“如果 Int(possibleNumber) 返回的可选 Int 包含一个值，创建一个叫做 actualNumber 的新常量并将可选包含的值赋给它。
    print("\'(possibleNumber)' hsa an interger value of \(actualNumber)")
}else{
    print("\'(possibleNumber)' could not be converted to an integer")
}

if let firstNumber = Int("4") , let secondNumber = Int("42") , firstNumber < secondNumber && secondNumber < 100{
    print("\(firstNumber) < \(secondNumber) < 100")
}
//上下出现的结果是一样的
if let firstNumber1 = Int("4") {
    if let secondNumber1 = Int("42") {
        if firstNumber1 < secondNumber1 && secondNumber1 < 100 {
            print("\(firstNumber1) < \(secondNumber1) < 100")
            
        }
    }
}

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹隐式解析可选类型
let possibleString:String? = "An optional string."
let forcedString:String = possibleString!//需要感叹号来获取值
/*
 如果你在隐式解析可选类型没有值的时候尝试取值，会触发运行时错误。和你在没有值的普通可选类型后面加一个惊叹号一样。
 如果一个变量之后可能变成nil的话请不要使用隐式解析可选类型。如果你需要在变量的生命周期中判断是否是nil的话，请使用普通可选类型
 */
let assumedString:String! = "An implcitly unwrapped optional string"
let implicitString:String = assumedString//不需要感叹号

if assumedString != nil {
    print(assumedString)
}
if let definiteString = assumedString {
    print(definiteString)
}

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹错误处理:当一个函数遇到错误条件，它能报错。调用函数的地方能抛出错误消息并合理处理。
/*
 一个函数可以通过在声明中添加throws关键词来抛出错误消息。当你的函数能抛出错误消息时, 你应该在表达式中前置try关键词。
 */
func canThrowAnError() throws{
    //这个函数有可能抛出错误
}

do {
    try canThrowAnError()
    //没有错误消息抛出
} catch  {
    //有一个错误消息抛出
}


func makeAnSandwich() throws{
    //...
}
func eatASanwich() {
    print("eat a Sandwich")
}
do {
    try makeAnSandwich()
    eatASanwich()
    
} catch {
    print("原料缺失")
}

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹断言
//使用断言进行调试
//let age = -3
//assert(age >= 0, "A person's age cannot be less than zero")
//assert(age >= 0)
//因为age< 0，所以会触发断言

//何时使用断言
/*
 当条件可能为假时使用断言，但是最终一定要保证条件为真，这样你的代码才能继续运行。断言的适用情景：
 
 整数类型的下标索引被传入一个自定义下标实现，但是下标索引值可能太小或者太大。
 需要给函数传入一个值，但是非法的值可能导致函数不能正常执行。
 一个可选值现在是 nil，但是后面的代码运行需要一个非 nil 值。
 */

 

//2⃣️2⃣️2⃣️2⃣️2⃣️2.2基本运算符(Basic Operators)2⃣️2⃣️2⃣️2⃣️2⃣️


//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹术语
/*
 *运算符分为：一元，二元，三元
 *一元针对单一对象操作：例如（-a, !b）等
 *二元针对两个操作对象：例如(2+3)等
 *三元针对三个操作对象：例如()
 */

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹赋值运算符：
//赋值运算符(a = b),表示用b的值来初始化或者更新a的值
let b2 = 10
var a2 = 5
a2 = b2

//如果赋值的右边是一个多元组，他的元素可以马上被分解成多个常量或者变量
let (x,y) = (1,2)//现在:x = 1,y = 2

/*与c语言和oc不同，swift的赋值操作并不返回任何值，所以以下代码是错误的
if x = y{
    
}
 */

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹算数运算符
/*Swift中所有的数值类型都支持了几本的四则算数运算符
 *加法(+),减法(-),乘法(*),除法(/)
1 + 2
5 - 3
2 * 3
10.0 ／ 2.5
 /*与c语言和oc不同的是，swift默认情况下不允许在数值运算中出现溢出情况。但是你可以使用swift的溢出运算符来实现溢出运算(a &+ b)*/
*/
"hello" + "world"//加法运算符也可用于String的拼接

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹求余运算符(a%b)
9 % 4

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹一元符号运算符

let minusSix = -6
let aisoMinusSix = +minusSix//一元正号符不做任何改变的返回操作数的值

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹组合赋值运算符
var aa2 = 1
aa2 += 2//aa2 = aa2 + 2 = 1 + 2 = 3


//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹比较运算符
/*
 *等于(a == b);不等于(a != b);大于(a > b);小于(a < b);大于等于(a >= b);小于等于(a <= b)
 *每个比较运算都返回了一个标识表达式是否成立的布尔值:
 *1 == 1//true；2 != 1//true；2 > 1//true；1 < 2//true;1 >= 1//true;2 <= 1／／false
 *
 */

//比较运算多用于条件语句
let name = "world"
if name == "world" {
    print("hello world")
}else{
    print("I'm sorry \(name),but I dont recognize you")
}
//比较元组的大小会按照从左到右，逐值比较的方式，直到发现有两个值不等时停止。如果所有的值都相等，那么这一对元组我们就称她们是相等的。
/*
 在上面的例子中，你可以看到，在第一行中从左到右的比较行为。因为1小于2，所以(1, "zebra")小于(2, "apple")，不管元组剩下的值如何
 */
(1 , "zebra") < (2 , "apple")//true，因为1 < 2
(3,"apple") < (3,"abple")
(3,"apple") < (3,"bbple")
(4, "dog") == (4, "dog")

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹三目运算符
/*
 *三目运算符的形式 :问题 ？ 答案1 : 答案2
 */
let contentHeight = 40
let hasHeader = true
let rowHeight = contentHeight + (hasHeader ? 50 : 20)

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹空合运算符(a ?? b)
/*如果a 包含一个值就进行解封，否则就返回一个默认值b。表达式a必须是optional。默认值b的类型必须要和a存储值的类型保持一致。
 */

let defaultColorName = "red"
var userDefinedColorName:String?

var colorNameToUse = userDefinedColorName ?? defaultColorName

userDefinedColorName = "green"
colorNameToUse = userDefinedColorName ?? defaultColorName


//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹区间运算符

//闭区间运算符(a...b)

for index in 1...5 {
    print("\(index)*5 = \(index * 5)")
}

//半开区间运算符(a..<b)
let names = ["Anna","Alex","Brain","Jack"]
let count = names.count
for i in 0..<count {
    print("第\(i + 1) 个人叫\(names[i])")
}

// 🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹逻辑运算符
/*
 *逻辑非(!a);逻辑与(a && b);逻辑或(a || b)

 */
//逻辑非运算
let allowedEntry = false
if !allowedEntry {
    print("ACCESS DENIED")
}


//逻辑与与运算符
let enteredDoorCode = true
let passedRetinaScan = false
if enteredDoorCode && passedRetinaScan {
    print("wwelcome")
}else{
    print("ACCESS DENIED")
}


//逻辑或运算
let hasDoorKey = false
let knowsOverridePassword = true
if hasDoorKey || knowsOverridePassword {
    print("welcome")
}else {
    print("ACCESS DENIEDE")
}

//逻辑运算符组合计算

if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
    print("Welcome")
}else{
    print("ACCESS DENIED")
}

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹使用括号来明确优先级

if (enteredDoorCode && passedRetinaScan) {
    print("Welcome")
}else{
    print("ACCESS DENIED")
}




//3⃣️3⃣️3⃣️3⃣️3⃣️2.3字符串和字符3⃣️3⃣️3⃣️3⃣️3⃣️

// 🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹字符串字面量可以用于为常量和变量提供初始值
let someString = "Some string literal value"

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹初始化空字符串
var emptyString = ""//空字符串字面量
var anotherEmptyString = String()//初始化方法

//可以通过检查其bool类型的isEmpty属性来判断该字符串是否为空
if emptyString.isEmpty {
    print("Nothing to see here")
}

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹字符串的可变性
//可以通过将一个特定字符串分配给一个变量来对其进行修改，或者分配给一个敞亮来保证其不会被修改
var variableString = "Horse"
variableString += " and carriage"//variableString现在为"Horse and carriage"
/*
 //报错，常量字符串不可修改
let constantString = "Hightander"
constantString += " and other Hightander "
*/


//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹字符串是值类型
//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹使用字符
//可以通过for-in循环遍历字符串中的characters属性来获取每一个字符的值

for character in "Dog1🐶".characters {
    print(character)
}

//通过标明一个Character类型并用字符字面量进行赋值，可以建立一个独立的字符常量或变量
let exclamtionMark:Character = "!"

//字符串可以通过传递一个值类型为Character的数组作为自变量来初始化
let catCharacters:[Character] = ["C","a","t","!","🐱"]
let catString = String(catCharacters)
print(catString)

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹连接字符串和字符
//字符串可以通过加法运算符(+)相加在一起创建一个新的字符串
let string3 = "Hello"
let string03 = " there"
var welcome =  string3 + string03

//也可以通过加法赋值运算符（+=）将一个字符串添加到一个已经存在字符串变量上
var instruction = "look over"
instruction += string03

//可以用，append()方法将一个字符串附加到一个字符串变量的尾部
//注意：不能将一个字符串或者字符添加到一个已经存在的字符变量上，因为字符变量只包含一个字符
welcome.append(exclamtionMark)


//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹字符串插值
let multiplier = 3

let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"


//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹Unicode
//字符串可以包含以下特殊字符：
/*
 转义字符\0(空字符),\\（反斜线）,\t（水平制表符）,\n（换行符）,\r（回车符）,\"（双引号）,\'（单引号）;
 Unicode标量，写成\u{n}(u为小写),其中n为任意一到八位十六进制数且可用的Unicode位码
 */
let wiseWord = "\"Imagination is more importsnt than knowledge\" - Einstein"
let dollarSign = "\u{24}"
let blackHeart = "\u{2665}"
let sparklightHeart = "\u{1f496}"


//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹可扩展的字形群集
//每一个swift的Xharacter类型代表一个可扩展的字形群，一个可扩展的字形群是一个活着多个可生成人类可读的字符串Unicode标量的有序排列
//1.这个字形群包含一个单一的标量
//2.它是包含两个标量的字形群
let eAcute:Character = "\u{E9}"
let combineEAcute:Character = "\u{65}\u{301}"

//可扩展的字符串水分灵活的方法，用许多复杂的脚本字符表示单一的Character值。
let precomposed:Character = "\u{D55C}"
let decomposed:Character = "\u{1112}\u{1161}\u{11AB}"

//可扩展的字符群集可以使包围记号的标量包围其他Unicode标量,作为一个单一的Character
let enclosedEAcute:Character = "\u{E9}\u{20DD}"

//地域性指示符号的Unicode标量可以组成一个单一的Character值
let regionalIndicatorForUS:Character = "\u{1F1FA}\u{1F1F8}"

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹计算字符数量
//使用Character的count属性
let unusualMenagerie = "Koala🐨,snail🐌,Penguin🐦,Dromedary🐫"
print("unusualMenagerie has \(unusualMenagerie.characters.count) characters")

var word03 = "cafe"
print("the number of charaters in \(word03) is \(word03.characters.count)")

word03 += "\u{301}"

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹访问和修改字符串
//字符串索引
/*
 *每一个String值都有一个关联的索引(index)类型，String.Index，它对应着字符串中的每一个Character的位置
 *使用startIndex属性可以获取一个String的第一个Character的索引。使用endIndex属性可以获取最后一个Character的后一个位置的索引。因此，endIndex属性不能作为一个字符串的有效下标。如果String是空串，startIndex和endIndex是相等的。
 *通过调用 String 的 index(before:) 或 index(after:) 方法，可以立即得到前面或后面的一个索引。您还可以通过调用 index(_:offsetBy:) 方法来获取对应偏移量的索引，这种方式可以避免多次调用 index(before:) 或 index(after:) 方法
 */
let greeting = "Guten Tag!"
greeting[greeting.startIndex]//G:startIndex属性可以获取一个String的第一个Character的索引
greeting[greeting.index(before: greeting.endIndex)]//!
greeting[greeting.index(after: greeting.startIndex)]//u
greeting[greeting.index(greeting.startIndex, offsetBy: 7)]//7:G往后的第7位

//试图获取越界索引对应的Character，将引发一个运行时的错误
//greeting[greeting.endIndex]//Can't form a Character from an empty String
//greeting.index(after: greeting.endIndex)//cannot increment beyond endIndex


//实验Characters属性的indices属性会创建一个包含全部索引的范围(Range)，用来在一个字符串中访问单个字符

for index in greeting.characters.indices {
    print("\(greeting[index])",terminator: "")
}

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹插入和删除
//调用insert(_:at)方法可以在一个字符串的指定索引插入一个字符，调用insert(contentsOf:at)方法可以在一个字符串的指定索引插入一个字符串
var welcome03 = "hello"
welcome03.insert("!", at: welcome03.endIndex)
welcome03.insert(contentsOf: " there".characters, at: welcome03.index(before: welcome03.endIndex))

//调用remove(at:)方法可以在一个字符串的指定索引删除一个字符，调用removeSubrabge(_:)方法可以在一个字符串的指定索引删除一个子字符串
welcome03.remove(at: welcome03.index(before: welcome03.endIndex))
print(welcome03)

let range = welcome03.index(welcome03.endIndex, offsetBy: -6)..<welcome03.endIndex
welcome03.removeSubrange(range)


//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹比较字符串

//swift提供了三种方式来比较文本值：字符串字符相等，前缀相等和后缀相等
//字符串／字符相等:可以用等于操作符（==）和不等于操作（！= ）
let quotation = "We're a lot alike ,you and I"
let sameQuotation = "We're a lot alike ,you and I"
if quotation == sameQuotation {
    print("These two string are considered equal")
}

//如果两个字符串（或者两个字符）的可扩展的字形群集是标准相等的，那就认为它们是相等的。在这个情况下，即使可扩展的字形群集是有不同的 Unicode 标量构成的，只要它们有同样的语言意义和外观，就认为它们标准相等。
let eActeQuestion = "Voulez-vous un caf\u{E9}?"
let combineEAcuteQuestion = "Voulez-vous un caf\u{65}\u{301}?"

if eActeQuestion == combineEAcuteQuestion {
    print("These two strings are considered equal")
}


//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹前缀／后缀相等
//通过hasPrefix(_:)/hasSuffix(_:)方法检查字符串是否拥有特定的前缀／后缀，两个方法均接收一个String类型的参数，并返回一个布尔值

let romeoAndJuliet = [
"Act 1 Scene1: Verona, A public place",
"Act 1 Scene2: Capulet's mansion",
"Act 1 Scene3: A room in Capulet's mansion",
"Act 1 Scene4: A street outside Capulet's mansion",
"Act 1 Scene5: The Great Hall in Capulet's mansion",
"Act 2 Scene1: Outside Capulet's mansion",
"Act 2 Scene2: Capulet's orchard",
"Act 2 Scene3: Outside Friar Lawrence's cell",
"Act 2 Scene4: A street in Verona",
"Act 2 Scene5: Capulet's mansion",
"Act 2 Scene6: Friar Lawrence's cell"
]
//可以调用hasPrefix(_:)方法来计算话剧中第一幕的场景数
var act1AceneCount = 0
for scene in romeoAndJuliet {
    if scene.hasPrefix("Act 1 ") {
        act1AceneCount += 1
    }
}
print("There are \(act1AceneCount) scenes in Act 1")

//相似的，可以用hasSuffix(_:)方法来计算发生在不同地方的场景数：
var mansionCount = 0
var cellCount = 0
for scene in romeoAndJuliet {
    if scene.hasSuffix("Capulet's mansion") {
        mansionCount += 1
    }else if scene.hasSuffix("Friar Lawrence's cell"){
        cellCount += 1
    }
}
print("\(mansionCount) mansion scenes; \(cellCount) cell scenes")


//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹字符串的 Unicode 表示形式
let dogString = "Dog!!🐶"

//utf8表示
for codeUnit in dogString.utf8 {
    print("\(codeUnit)" , terminator: "")
}
print("")

//utf16表示
for codUnit in dogString.utf16 {
    print("\(codUnit)" , terminator: "")
}
print("")



//3⃣️3⃣️3⃣️3⃣️3⃣️3⃣️集合类型3⃣️3⃣️3⃣️3⃣️3⃣️
//swift语言提供Array,Sets和Dictionaries三种基本的集合类型用来存储集合数据。数组(Array)是有序数据的集。集合(Sets)是无序无重复数据的集。字典(Dictionaries)是无序的键值对的集。
//集合的可变性：设置为常量就不可改变；设置为变量就可改变
//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹数组(Arrays):数组使用有序列表存储同一类型的多个值，相同的值可以多次出现在一个数组的不同位置
//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹创建一个空数组
var someInts = [Int]()//创建一个元素是 Int类型的空数组

someInts.append(3)//someInts现在包含一个Int值
someInts = []//someInts现在为空

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹创建一个带有默认值的数组
//Swift 中的Array类型还提供一个可以创建特定大小并且所有数据都被默认的构造方法。我们可以把准备加入新数组的数据项数量（count）和适当类型的初始值（repeating）传入数组构造函数：
var threeDoubles = Array(repeating: 0.0, count: 2)//[0,0]

//通过两个数组相加创建一个数组
//我们可以使用操作符(+)来组合两种已经存在的相同类型数组。新数组的数据类型会被从两个数组的数据类型中推断出来
var anotherThreeDoubles = Array(repeating: 2.5, count: 3)//[2.5, 2.5, 2.5]
var fiveDoubles = threeDoubles + anotherThreeDoubles//[0, 0, 2.5, 2.5, 2.5]


//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹用数组字面量来构造数组：数组字面量是一系列由逗号分隔并由方括号包含的值
var shoppingList:[String] = ["Eggs" , "Milk"]

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹访问和修改数组
//可以使用数组的只读属性count来获取数组中的数据项数量
print("The shopping list contains \(shoppingList.count) items")

//使用布尔属性isEmpty作为一个缩写形式去检查count 属性是否为0
if shoppingList.isEmpty{
    print("The shopping list is empty.")
}else{
    print("The shopping list is not empty.")
}

//使用append(_:)方法在数组后面添加新的数据项：
shoppingList.append("Flour")

//使用加法赋值运算符(+=)可以直接在数组后面添加一个或多个拥有相同类型的数据项：
shoppingList += ["Baking Powder"]

shoppingList += ["Chocolate Spread","Cheese","Butter"]

//可以直接使用下标语法来获取数组中的数据项
var firstItem = shoppingList[0]

//我们也可以用下标来改变某个已经有索引值对应的数据值
shoppingList[0] = "Six EGG"

//还可以用下标来一次改变一系列的数据值
shoppingList[4...6] = ["Bannanas" , "Apples"]
print(shoppingList)

//调用 insert(_:at)方法来在某个具体索引值之前添加数据项
shoppingList.insert("Maple Surup", at: 0)
print(shoppingList)

//移除数组中的某一项
let mapleSurup = shoppingList.remove(at: 0)
print(shoppingList)

//数据项被移除后数组中的空出项会被自动填补，所以索引值为0的数据项的值再次等于"Six eggs"
firstItem = shoppingList[0]

//如果想把数组中的最后一项移除，可以使用removeLast()方法不是remove(_:at)
let apples = shoppingList.removeLast()

//数组的遍历
//我们可以用for-in循环来遍历所有数组中的数据项
for item in shoppingList{
    print(item)
}

//如果我们同时需要每个数据项的值和索引值，可以使用enumerated()方法来进行数组遍历。
for (index,value) in shoppingList.enumerated(){
    print("Item \(String(index + 1)) :\(value)")
}

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹集合Sets
//集合Set用来存储相同类型并且没有确定顺序的值
//集合类型语法:Set<Element>

//创建和构造一个空的集合
var letters = Set<Character>()
print("letters is of type Set<Character> with \(letters.count) items")

letters.insert("a")

letters = []

//用数组字面量创建集合
var favoriteGenres:Set<String> = ["Rock","Classic","Hip hop"]

//访问和修改一个集合
//使用布尔属性isEmpty作为一个缩写形式去检查count属性是否为0
if favoriteGenres.isEmpty
{
    print("As far as music goes ,I'm not picky")
}else{
    print("I have particular music preferences")
}
//可以通过insert(_:)方法来添加一个新的元素
favoriteGenres.insert("Jazz")

//删除remove(_:)
if let removedGenre = favoriteGenres.remove("Rock"){
    print("\(removedGenre)? I'm over it")
}else{
    print("I never much cared for that")
}

//使用contains(_:)去检查set 中是否包含一个特定的值
if favoriteGenres.contains("Funk"){
    print("I get up on the good foot")
}else{
    print("It's too funky in here")
}


//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹遍历一个集合
//可以在一个for-in循环中遍历一个Set中所有的值
for genre in favoriteGenres{
    print("\(genre)")
}

let aaa:Set<Int> = [1,2,3,4,5]
let bbb:Set<Int> = [3,4,5,6,7]

//集合操作
//a与b的各种操作
/*
 使用intersection(_:)方法根据两个集合中都包含的值创建的一个新的集合。
 使用symmetricDifference(_:)方法根据在一个集合中但不在两个集合中的值创建一个新的集合。
 使用union(_:)方法根据两个集合的值创建一个新的集合。
 使用subtracting(_:)方法根据不在该集合中的值创建一个新的集合
*/


let oddDigits:Set = [1,3,5,7,9]
let evenDigits:Set = [0,2,4,6,8]

let singleDigitPrimeNumber:Set = [2,3,5,7]

oddDigits.union(evenDigits).sorted()
oddDigits.intersection(evenDigits).sorted()
oddDigits.subtracting(singleDigitPrimeNumber).sorted()
oddDigits.symmetricDifference(singleDigitPrimeNumber).sorted()

//集合成员关系和相等
/*
 使用“是否相等”运算符(==)来判断两个集合是否包含全部相同的值。
 使用isSubset(of:)方法来判断一个集合中的值是否也被包含在另外一个集合中。
 使用isSuperset(of:)方法来判断一个集合中包含另一个集合中所有的值。
 使用isStrictSubset(of:)或者isStrictSuperset(of:)方法来判断一个集合是否是另外一个集合的子集合或者父集合并且两个集合并不相等。
 使用isDisjoint(with:)方法来判断两个集合是否不含有相同的值(是否没有交集)。
 */

let houseAnimal:Set = ["🐶","🐱"]
let farmAnimals:Set = ["🐂","🐔","🐑","🐶","🐱"]
let cityAnimals:Set = ["🐦","🐭"]

houseAnimal.isSubset(of: farmAnimals)
farmAnimals.isSuperset(of: houseAnimal)
farmAnimals.isDisjoint(with: cityAnimals)

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹字典
//创建一个空字典
var nameOfIntegers = [Int:String]()//一个空的[Int:String]字典
nameOfIntegers[16] = "sixteen"
print(nameOfIntegers)

nameOfIntegers = [:]

//用字典字面量创建字典
var airports:[String:String] = ["YYZ":"Toronto Person" , "DUB":"Dublin"]

//与上面的相等 ：var airports = ["YYZ":"Toronto Person" , "DUB":"Dublin"]

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹访问和修改字典
print("The dictionary of airports contains \(airports.count) items")

//使用布尔属性isEmpty作为一个缩写形式去检查count属性是否为0
if airports.isEmpty{
    print("The airport dictionary is empty")
}else {
    print("The airport dictionary is  not empty")
}

//我们也可以在字典中适应下标语法来添加新的数据项
airports["LHR"] = "London"

//我们也可以使用下标语法来改变特定的键对应的值
airports["LHR"] = "London Heathrow"

//作为另一种下标方法，字典的updateValue(_:forkey:) 方法，可以设置或者更新特定键对应的值。就像上面所示的下标示例
if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB"){
    print("The old value for DUB was \(oldValue)")
}

if let airportName = airports["DUB"] {
    print("The name of the airport is \(airportName)")
}else{
    print("That airport is not in the airports dictionary.")
}

//我们还可以使用下标语法来通过给某个键的对应值赋值为nil来从字典中移除一个键值
airports["APL"] = "Apple Internation"

airports["APL"] = nil//被移除了

//removeValue(forkey:)方法也可以用来在字典中移除键值对。这个方法在键值对在的情况下会移除该键值对并且返回被移除的值或者在没有值的情况下返回nil

if let removeValue = airports.removeValue(forKey: "DUB") {
    print("The removed airport's name is \(removeValue).")

}else{
    print("The airports dictionary does not contain a value for DUB.")
}


//字典遍历

for (airportCode, airportName) in airports {
    print("\(airportCode): \(airportName)")
}

for airportCode in airports.keys {
    print("Airport code: \(airportCode)")
}
// Airport code: YYZ
// Airport code: LHR

for airportName in airports.values {
    print("Airport name: \(airportName)")
}

let airportCodes = [String](airports.keys)
// airportCodes 是 ["YYZ", "LHR"]

let airportNames = [String](airports.values)
// airportNames 是 ["Toronto Pearson", "London Heathrow"]


//4⃣️4⃣️4⃣️4⃣️4⃣️4⃣️  控制流(Control Flow) 4⃣️4⃣️4⃣️4⃣️4⃣️

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹 For-In循环
//可以使用for-in循环遍历一个集合中的所有元素

for index in 1...5 {
    print("\(index) times 5 is \(index * 5)")
}

//区间
/*
 *大于等于1且小于等于5:(1...5)
 *大于等于1且小于5:(1..<5)
 */

//如果你不需要区间序列内每一项的值，你可以使用下划线（_）替代变量名来忽略这个值
let base = 3
let power = 10
var answer = 1
for _ in 1...power {
    answer *= base
}
print("\(base) to the power of \(power) is \(answer)")


//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹使用for-in遍历一个数组的所有元素
let names = ["Anna","Alex","Brian","Jack"]
for name in names {
    print("Hello ,\(name)")
}

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹可以通过遍历一个字典访问它的键值对;遍历字典时，字典的每项元素会以(key,value)元组的形式返回
//字典元素的遍历顺序和插入顺序可能不同，字典的内容在内部是无序的，所以遍历元素时不能保证顺序
let numberOfLegs = ["spider":8,"ant":6,"cat":4]
for (animalName,legCount) in numberOfLegs {
    print("\(animalName)s have \(legCount) legs")
}


//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹While循环
//While循环，每次在循环开始时计算条件是否符合
//repeat-while循环，每次在循环结束时计算条件是否符合
/*
let finalSquare = 25
var board = [Int](repeatElement(0, count: finalSquare + 1))

board[03] = +08;board[06] = +11;board[09] = +09;board[10] = +02
board[14] = -10;board[19] = -11;board[22] = -02;board[24] = -08

var square = 0
var diceRoll = 0

while square < finalSquare {
    //掷骰子
    diceRoll += 1
    if diceRoll == 7 {
        diceRoll = 1
    }
    //根据点数移动
    square += diceRoll
    if square < board.count {
        //如果玩家还在棋盘上，顺着梯子爬上去或者顺着蛇滑下去
        square += board[square]
    }
}
print("Game Over")

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹Repeat-While
//在判断循环条件之前先执行一次循环的代码块

repeat {
    // 顺着梯子爬上去或者顺着蛇滑下去
    square += board[square]
    // 掷骰子
    diceRoll += 1
    if diceRoll == 7 { diceRoll = 1 }
    // 根据点数移动
    square += diceRoll
} while square < finalSquare
print("Game over!")
*/
//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹条件语句
//if
var temperatureInFathrenheigt = 30
if temperatureInFathrenheigt <= 32{
    print("It's very cold. Consider wearing a scarf.")
}


//if-else:允许二选一执行
temperatureInFathrenheigt = 40
if temperatureInFathrenheigt <= 32 {
    print("It's very cold. Consider wearing a scarf.")
}else{
    print("It's not that cold. Consider wearing a scarf.")
}

//可以把多个if语句链接在一起，实现更多的分支
temperatureInFathrenheigt = 90
if temperatureInFathrenheigt <= 32 {
    print("It's very cold. Consider wearing a scarf.")
}else if temperatureInFathrenheigt >= 86 {
    print("It's very really warm. Don't forget to wear a sunscreen.")
    
}else {
    print("It's not that cold. Wear a t-shirt")
}

//实际上，当不需要完整判断情况的时候，最后的else语句是可选的
temperatureInFathrenheigt = 72
if temperatureInFathrenheigt <= 32 {
    print("It's very cold. Consider wearing a scarf.")
} else if temperatureInFathrenheigt >= 86 {
    print("It's really warm. Don't forget to wear sunscreen.")
}

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹Switch
//switch语句会尝试把某个值与若干个模式进行匹配，根据第一个匹配成功的模式，switch会执行相应的代码，当有可能的情况较多时，通常用switch语句替换if语句

//switch语句使用default分支来匹配除了a和z外的所有值
let someCharacter:Character = "z"
switch someCharacter {
case "a":
    print("The first letter of the alphabet")
case "z":
    print("The last letter of the alphabet")
default:
    print("some other charater")
}

//不存在隐式的贯穿
//每一个 case 分支都必须包含至少一条语句
//为了让单个case同时匹配a和A，可以将这个两个值组合成一个复合匹配，并且用逗号分开：
let anotherCharacter:Character = "a"

switch anotherCharacter {
case "a","A":
    print("The letter is A")
default:
    print("Not the letter A")
}

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹区间匹配
//case分支的模式也可以是一个值的区间
let approximateCount = 62
let countedThings = "moons orbiting scaturn"
var naturalCount:String

switch approximateCount {
case 0:
    naturalCount = "no"
case 1..<5:
    naturalCount = "a few"
case 5..<12:
    naturalCount = "several"
case 12..<100:
    naturalCount = "dozens of"
case 100..<1000:
    naturalCount = "hunareds of"
default:
    naturalCount = "many"
}

print("There are \(naturalCount) \(countedThings)")


//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹元组
//我们可以使用元组在同一个switch语句中测试多个值，元组中的元素可以是值，也可以是区间，另外，使用下划线(_)来匹配所有可能的值
let somePoint = (1,1)
switch somePoint {
case (0,0):
    print("(0,0) is at the origin")
case (_,0):
    print("(\(somePoint.0),0) is on the x-axis")
case (0,_):
    print("(0,\(somePoint.1)) is on the y-axis")
case (-2...2,-2...2):
    print("(\(somePoint.0),\(somePoint.1)) is inside the box")
default:
     print("(\(somePoint.0), \(somePoint.1)) is outside of the box")
}

//值绑定
//case分支允许将匹配的值绑定到一个临时的变量和常量，并且在分支体内使用

let anotherPoint = (2,0)
switch anotherPoint {
case (let x , 0):
    print("on the x-axis with an x value of \(x)")
case (0 , let y):
    print("on the y-axis with an x value of \(y)")
case (let x ,let y):
    print("on the x-axis with an x value of (\(x),\(y))")
}

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹Where
//case的分支的模式可以使用where语句来判断额外的条件
let yetAnotherPoint = (1,-1)
switch yetAnotherPoint{
case let (x,y) where x == y:
    print("(\(x),\(y)) is on the line x == y")
case let (x,y) where x == -y:
    print("\(x),\(y) is on the line x == -y")
case let (x,y):
    print("\(x),\(y) is just some arbitrary point")
}

//符合匹配
//多个条件可以使用同一种方法来处理时，可以将这几种可能放在同一个case后面并且用逗号隔开，当case后面的任意一种模式匹配的时候这条分支就会匹配，且条件过长的话还可以分行写
let someCharacters:Character = "e"
switch someCharacters {
case "a","e","i","o","u":
    print("\(someCharacters) is a vowel")
case "b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","y","z":
    print("\(someCharacters) is a consonant")
default:
    print("\(someCharacters) is not a vowel or a consonant")
}

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹控制转移语句
/*swift
 *continue;break;fallthrough;return
 */

//Continute：告诉一个循环体立刻停止本次循环，重新开始下次循环

let puzzleInput = "great minds think alike"
var puzzleOutput = ""
for character in puzzleInput.characters {
    switch character {
    case "a","e","i","o","u"," ":
        continue
    default:
        puzzleOutput.append(character)
    }
}
//在上面的代码中，只要匹配到元音字母或者空格字符，就调用continue语句，使本次循环结束，重新开始下次循环。这种行为使switch匹配到元音字母和空格字符时不做处理，而不是让每一个匹配到的字符都被打印。
print(puzzleOutput)

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹Break
//break语句会立刻结束整个控制行的执行。当你想要更早结束一个swift代码活着一个循环体的时候，你可以使用break语句

//循环语句中的 break
//当在一个循环体中使用break时，会立刻中断该循环体的执行，然后跳转到表示循环体结束的大括号(})后的第一行代码。不会再有本次循环的代码被执行，也不会再有下次的循环产生。

//switch语句中的循环
let  numberSymbol:Character = "三"
var possibleIntegerValue:Int?
switch numberSymbol {
case "1", "١", "一", "๑":
    possibleIntegerValue = 1
case  "2", "٢", "二", "๒":
    possibleIntegerValue = 2
    case "3", "٣", "三", "๓":
    possibleIntegerValue = 3
    case "4", "٤", "四", "๔":
    possibleIntegerValue = 4
default:
    break
}
if let interValue = possibleIntegerValue {
    print("The integer value of \(numberSymbol) is \(interValue)")
}else{
    print("An integer value could not be found for \(numberSymbol).")

}

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹贯穿

//swift中的swift不会从上一个case分支落入下一个case分支中，相反，只要第一个匹配到的case分支完成了他需要执行的语句，整个swift代码块完成了他的执行。相比之下，c语言要求你显式地插入break语句到每个case分支的末尾来阻止自动落入到下一个case分支中。
//可以在每个需要贯穿的case分支中使用fallthrough关键字
let intergerToDescribe = 5
var description = "The number \(intergerToDescribe) is"

switch intergerToDescribe{
case 2,3,5,7,11,13,17,19:
    description += " a prime number ,and also"
    fallthrough
default:
    description += " an integer"
}

print(description)

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹带标签的语句
//如果某次掷骰子使你的移动超出第 25 个方块，你必须重新掷骰子，直到你掷出的骰子数刚好使你能落在第 25 个方块中。
/*
let finalSquare = 25
var board = [Int](repeating: 0, count: finalSquare + 1)
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
var square = 0
var diceRoll = 0

gameLoop:while square != finalSquare{
    diceRoll += 1
    if diceRoll == 7 {
        diceRoll = 1
    }
    
    switch square + diceRoll {
    case finalSquare:
        //骰子数刚好使玩家移动到最终的芳菲里面，游戏结束
        break gameLoop
    case let newSquare where newSquare > finalSquare:
        //骰子数将会使玩家的移动超出最后的方格，那么这种移动是不合法的，玩家需要重新掷骰子
        continue gameLoop
    default:
        //合法移动，做正常处理
        square += diceRoll
        square += board[square]
    }
}
print("Game over!")
*/

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹提前退出
//guard 
func greet (person:[String:String]){
    guard let name = person["name"] else {
        return
    }
    print("Hello \(name)")
    
    guard let location = person["location"] else {
        print("I hope the weather is nice near you")
        return
    }
     print("I hope the weather is nice in \(location)")
}

greet(person: ["name":"John"])
greet(person: ["name":"John","location":"Cupertino"])

//如果guard语句的条件语句被满足，则继续执行guard语句大括号后面的代码，将变量或者常量的可选绑定作为guard语句的条件，都可以保护guard语句后面的代码；如果条件不被满足，在else分支上的代码就会被执行，这个分支必须转移控制以退出guard语句出现的代码段。


//🌹🌹🌹🌹🌹🌹🌹🌹🌹检测API可用性
//swift内置支持检查API可用性，这可以保证我们不会在当前部署的机器上，不小心使用不可用的API。
if #available(iOS 10, macOS 10.12 , *){
    //在iOS使用iOS10的API，在macOS使用macOS 10.12 的 API
}else{
    //使用先前版本的iOS和macOS的API
}
//在它一般的形式中，可用性条件使用了一个平台名字和版本的列表。平台名字可以是iOS，macOS，watchOS和tvOS——请访问声明属性来获取完整列表。除了指定像 iOS 8的主板本号，我们可以指定像iOS 8.3 以及 macOS 10.10.3的子版本号。

if #available(platform name version, ..., *) {
  //  APIs 可用，语句将执行
} else {
 //   APIs 不可用，语句将不执行
}



//6⃣️6⃣️6⃣️6⃣️6⃣️  函数  6⃣️6⃣️6⃣️6⃣️6⃣️
//函数：是一段完成特定任务的独立代码片段
//函数的定义与调用：
func greet(person:String) -> String{
    let greeting = "Hello " + person + "!"
    return greeting
}
print(greet(person: "Anna"))
print(greet(person: "Brain"))
//调用 greet(person:) 函数时，在圆括号中传给它一个 String 类型的实参,输出这个函数的返回值
//为了简化这个函数定义，可以将问候消息的创建和返回写成一句话
func greetAgain (person:String) -> String{
    return "Hello again," + person + "!"
}
print(greet(person: "Anna"))

//函数参数与返回值
//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹无参数函数
//函数可以没有参数。下面这个函数就是一个无参数函数，当被调用时，它返回固定的 String 消息
func sayHelloWorld() -> String{
    return "hello world"
}
print(sayHelloWorld())
//尽管这个函数没有参数，但是定义中在函数名后还是需要一对圆括号。当被调用时，也需要在函数名后写一对圆括号

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹多参数函数

//函数可以又多种输入参数，这些参数被包含在函数的括号之中，以逗号分隔
//下面这个函数用一个人名和是否已经打过招呼作为输入，并返回对这个人的适当问候语:
func greetmore(person:String,alreadyGreeted:Bool) -> String{
    if alreadyGreeted {
        return greetAgain(person: person)
    }else {
        return greet(person: person)
    }
}

print(greetmore(person: "Tim", alreadyGreeted: true))

//无返回值函数
func greet1(person:String){
    print("Hello , \(person)")
}
greet1(person: "Dave")

//被调用时，一个函数的返回值可以被忽略
func printAndCount(string:String) -> Int{
    print(string)
    return string.characters.count
}
func printWithoutCounting(string:String){
    let _ = printAndCount(string: string)
    
}
printAndCount(string: "hello , world")
printWithoutCounting(string: "hello, world")

//多重返回值函数
func minMax(array:[Int]) -> (min:Int,max:Int){
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1..<array.count] {
        if value < currentMin {
            currentMin = value
        }else if value > currentMax{
            currentMax = value
        }
    }
    return(currentMin,currentMax)
    
}

let bounds = minMax(array: [8,-6,9,3,22,45])
print("min is \(bounds.min) and max is \(bounds.max)")

//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹可选元组返回类型
//如果函数返回的元组类型可能整个元组都“没有值”，你可以使用可选的元组返回类型反映整个元组可以是nil的事实，你可以通过在元组类型的右括号后放置一个问号来定义一个可选元组，例如(Int,Int)?或(String,Int,Bool)?

func minMax1(array:[Int]) -> (min:Int,max:Int)?{
    if array.isEmpty {
        return nil
    }
    var currentMin = array[0]
    var currentMax = array[0]
    
    for value in array[1..<array.count] {
        if value < currentMin {
            currentMin = value
        }else if value > currentMax{
            currentMax = value
        }
    }
    
    return (currentMin,currentMax)
    
}

if let bounds1 = minMax1(array: [8,-6,9,0,34]) {
    
    print("min is \(bounds1.min) and max is \(bounds1.max)")

}


//🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹函数参数标签和参数名称

func someFunction(firstParameterName:Int,secondParameterName:Int){
    //在函数体内，firstParameterName和secondParameterName代表参数重的第一个和第二个参数值
}
someFunction(firstParameterName: 1, secondParameterName: 2)
//指定参数标签
func greethometown(person:String,from hometown:String) -> String{
    return("Hello \(person)! Glad you could visit from \(hometown)")
}
print(greethometown(person: "Bill", from: "Cupertiop"))

//忽略参数标签
func someFunction1(_ firstParameterName: Int, secondParameterName: Int) {
    // 在函数体内，firstParameterName 和 secondParameterName 代表参数中的第一个和第二个参数值
}
someFunction1(1, secondParameterName: 2)

//默认参数值
//你可以在函数体中通过给参数赋值来为任意一个参数定义默认值。
func someFunctiondefault(parameterWithoutDefault:Int,parameterWithDefault:Int = 12){
    //如果你在调用的时候不传第二个参数，parameterWithDefault会值为12传入函数体中
}
someFunctiondefault(parameterWithoutDefault: 3,parameterWithDefault: 6)//parameterWithDefault值为6
someFunctiondefault(parameterWithoutDefault: 4)//parameterWithDefault值为12

//可变参数
//一个可变参数可以接受零个或多个值，函数调用时，你可以用可变参数来指定函数可以被传入不确定数量的输入值。通过在变量类型名后面加入(...)的方式来定义可变参数
//可变参数的传入值在函数体中变为此类型的一个数组。例如，一个叫做 numbers 的 Double... 型可变参数，在函数体内可以当做一个叫 numbers 的 [Double] 型的数组常量。
func arithmeticMean(_ numbers:Double...) -> Double{
    var total:Double = 0
    for number in numbers {
        total += number
    }
    return total/Double(numbers.count)
    
}
arithmeticMean(1,2,3,4,5)
arithmeticMean(3,6,34,19.00)

//一个函数最多只能拥有一个可变参数
//输入输出参数,只能传递变量给输入输出函数
func swapTwoInts(_ a: inout Int, _ b: inout Int){
    let temporaryA = a
    a = b
    b = temporaryA
    
}

var someInt = 3
var anotherInt = 107
swap(&someInt, &anotherInt)
print("someInt is now \(someInt),and anotherInt is now \(anotherInt)")

//函数类型
func addTwoInts(_ a: Int ,_ b:Int) -> Int{
    return a + b
}

func multiplyTwoInts(_ a:Int,_ b:Int) -> Int{
    return a*b
}

//使用函数类型
//在 Swift 中，使用函数类型就像使用其他类型一样。例如，你可以定义一个类型为函数的常量或变量，并将适当的函数赋值给它：
var mathFunction:(Int,Int) -> Int = addTwoInts(_:_:)
print("Result:\(mathFunction(2,3))")

//有相同匹配类型的不同函数可以被赋值给同一个变量，就像非函数类型的变量一样：

mathFunction = multiplyTwoInts
print("Result: \(mathFunction(2, 3))")
//就像其他类型一样，当赋值一个函数给常量或变量时，你可以让 Swift 来推断其函数类型：
 //函数类型作为参数类型
 func printMathResult(_ mathFunchtion:(Int,Int) -> Int,_ a:Int,_ b: Int){
 print("Result:\(mathFunction(a,b))")
 }
 printMathResult(addTwoInts(_:_:), 3, 5)
 
 //函数类型作为返回值
 func stepForward(_ input:Int) -> Int{
 return input + 1
 }
 func stepBackward(_ input:Int) -> Int{
 return input - 1
 }
 
 //如下名为 chooseStepFunction(backward:) 的函数，它的返回类型是 (Int) -> Int 类型的函数。chooseStepFunction(backward:) 根据布尔值 backwards 来返回 stepForward(_:) 函数或 stepBackward(_:) 函数：
 func chooseStepFunction(backward:Bool) -> (Int) -> Int{
 return backward ? stepBackward(_:) : stepBackward(_:)
 }
 
 //现在可以用chooseStepFunction(backward:) 来获得两个函数其中的一个
 var currentValue = 3
 let moreNearerToZero = chooseStepFunction(backward: currentValue > 0)
 
 print("Count to zero:")
 
 while currentValue != 0 {
 print("\(currentValue)...")
 currentValue = moreNearerToZero(currentValue)
 }
 
 
 //嵌套函数
 
 //到目前为止本章中你所见到的所有函数都叫全局函数（global functions），它们定义在全局域中。你也可以把函数定义在别的函数体中，称作 嵌套函数（nested functions）。
 
 //默认情况下，嵌套函数是对外界不可见的，但是可以被它们的外围函数（enclosing function）调用。一个外围函数也可以返回它的某一个嵌套函数，使得这个函数可以在其他域中被使用。
 func chooseStepFunction(back:Bool) -> (Int) -> Int{
 func stepForward1(input: Int) -> Int {
 return input + 1
 }
 func stepBackward1(input: Int) -> Int {
 return input - 1
 }
 return back ? stepBackward1(input:) : stepForward1(input:)
 }
 
 var current = -4
 let moveNearerToZero = chooseStepFunction(back: current > 0)
 
 while current != 0 {
 print("\(current)...")
 current = moveNearerToZero(current)
 }
 print("Zero")
 
 
 
 import UIKit
 
 //闭包
 //闭包表达式
 //闭包是自包含的函数代码块，可以在代码中被传递和使用，闭包可以捕捉和存储其所在上下文中任意常量和变量的饮用。
 /*
 *闭包的三种形式
 *全局函数是一个有名字但是不会捕获任何值的闭包
 *嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包
 *闭包表达式是一个利用轻量级语法所写的可以捕获其上下文中变量或者常量值的匿名闭包
 */
 
 let names = ["Chris","Alex","Ewa","Barry","Daniella"]
 
 func backward(_ s1: String,_ s2: String) -> Bool {
 return s1 > s2
 }
 var reversedNames = names.sorted(by: backward)
 
 //函数对应的闭包表达式
 reversedNames = names.sorted(by: { (s1:String, s2:String) -> Bool in
 return s1 > s2
 })
 
 //根据上下文推断类型
 //通过内联闭包作为参数传递给函数或者方法时，总能推断出闭包的参数和返回值类型，这意味着闭包作为函数活着方法的参数时，你几乎不需要利用完整格式构造内联闭包
 reversedNames = names.sorted(by: {s1,s2 in return s1 > s2})
 
 //参数名称缩写
 //如果你在闭包表达式中使用参数名称缩写，你可以在闭包定义中省略参数列表，并且对应参数名称缩写的类型会通过函数类型进行推断。in关键字也同样可以被省略，因为此时闭包表达式完全由闭包函数体构成
 reversedNames = names.sorted(by: {$0>$1})
 
 
 //运算符方法
 reversedNames = names.sorted(by: >)
 
 
 //尾随闭包
 //如果你需要将一个很长的闭包表达式作为最后一个参数传递给函数，可以使用尾随闭包来增强函数的可读性。尾随闭包是一个书写在函数括号之后的闭包表达式，函数支持将其作为最后一个参数调用
 func someFunctionThatTakesAClosure(closure: () -> Void){
 //函数体部分
 }
 
 //以下是不使用尾随闭包进行函数调用
 someFunctionThatTakesAClosure (closure: {
 //闭包主体部分
 })
 
 //以下是使用尾随闭包进行函数调用
 someFunctionThatTakesAClosure() {
 //闭包主体部分
 }
 
 let digitNames = [
 0:"Zero",1:"One",2:"Two",3:"Three",4:"Four",
 5:"Five",6:"Six",7:"Seven",8:"Eight",9:"Nine"
 ]
 
 let numbers = [16,58,108]
 //如上代码创建了一个整型数位和它们英文版本名字相映射的字典。同时还定义了一个准备转换为字符串数组的整型数组。你现在可以通过传递一个尾随闭包给 numbers 数组的 map(_:) 方法来创建对应的字符串版本数组：
 let strings = numbers.map {
 (number) -> String in
 var number = number
 var output = ""
 repeat{
 output = digitNames[number % 10]! + output
 number /= 10
 
 }while number > 0
 return output
 }
 print(strings)
 
 //值捕获
 func makeIncrementer(forIncrement amount:Int) ->() ->Int{
 var runningTotal = 0
 func incrementer() -> Int{
 runningTotal += amount
 return runningTotal
 }
 return incrementer
 
 }
 //makeIncrementor 返回类型为 () -> Int。这意味着其返回的是一个函数，而非一个简单类型的值。该函数在每次调用时不接受参数，只返回一个 Int 类型的值。
 
 let incrementByTen = makeIncrementer(forIncrement: 10)
 
 //逃逸闭包
 //当一个闭包作为参数传到一个函数中，但是这个闭包在函数返回之后才被执行，我们称该闭包从函数中逃逸。当你定义接受闭包作为参数的函数时，你可以在参数名之前标注 @escaping，用来指明这个闭包是允许“逃逸”出这个函数的。
 var completionHandlers:[() -> Void] = []
 func someFunctionWithEscapingClosure(completionHandler:@escaping () -> Void){
 completionHandlers.append(completionHandler)
 }
 
 func someFunctionWithNonescapingClosure(closure: () -> Void){
 closure()
 }
 class SomeClass{
 var x = 10
 func doSomething() {
 someFunctionWithEscapingClosure {
 self.x = 100
 }
 someFunctionWithNonescapingClosure {
 x = 200
 }
 
 }
 
 }
 
 let instance = SomeClass()
 instance.doSomething()
 print(instance.x)
 
 
 //自动闭包
 //自动闭包是一种自动创建的闭包，用于包装传递给函数作为参数的表达式。这种闭包不接受任何参数，当他被调用的时候，会返回被包装在其中的表达式的值。
 //自动闭包让你能够延迟求值，因为直到你调用这个闭包，代码段才会被执行。延迟求值对于那些有副作用（Side Effect）和高计算成本的代码来说是很有益处的，因为它使得你能控制代码的执行时机。下面的代码展示了闭包如何延时求值。
 var customersInLine = ["Chris","Alex","Ewa","Barry","Daniella"]
 print(customersInLine.count)
 
 let customerProvider = {customersInLine.remove(at: 0)}
 print(customersInLine.count)
 print("Now serving \(customerProvider())")
 print(customersInLine.count)
 //尽管在闭包的代码中，customersInLine 的第一个元素被移除了，不过在闭包被调用之前，这个元素是不会被移除的。如果这个闭包永远不被调用，那么在闭包里面的表达式将永远不会执行，那意味着列表中的元素永远不会被移除
 
 //将闭包作为参数传递给函数时，你能获得同样的延时求值行为
 func serve(customer customerProvider:() -> String){
 print("Now serving\(customerProvider())")
 }
 serve(customer: {customersInLine.remove(at: 0)})
 
 
 // customersInLine is ["Ewa", "Barry", "Daniella"]
 func serve(customer customerProvider: @autoclosure () -> String) {
 print("Now serving \(customerProvider())!")
 }
 serve(customer: customersInLine.remove(at: 0))
 // 打印 "Now serving Ewa!"
 
 // customersInLine i= ["Barry", "Daniella"]
 var customerProviders: [() -> String] = []
 func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -> String) {
 customerProviders.append(customerProvider)
 }
 collectCustomerProviders(customersInLine.remove(at: 0))
 collectCustomerProviders(customersInLine.remove(at: 0))
 
 print("Collected \(customerProviders.count) closures.")
 // 打印 "Collected 2 closures."
 for customerProvider in customerProviders {
 print("Now serving \(customerProvider())!")
 }
 // 打印 "Now serving Barry!"
 // 打印 "Now serving Daniella!"
 
 


*/
